#!/bin/bash 
########################################################################################
# sbt-spine [Name] [-vex] [-git|-ext [path]]
#   Name: project Name, "test" if not given
#   -vex: project relay on VexRiscv
#   -run: issue "sbt run"  
#   -git/ext:
#         if project relay on VexRiscv, using
#	  -git to given target VexRiscv from a git address
#	  -ext to given a local directory path of VexRiscv
#	  if no path givem, default:
#	    git://github.com/SpinalHDL/VexRiscv.git
#	    ext:./ext/VexRiscv
#	  -ext path can be absolute or relative to created project
#
#  e.g:
#  $ spt-spine hello -vex -ext ../VexRiscv
#	- new project named 'hello' created
#	- project relay on VexRiscv
#	- VexRiscv is locate at VexRiscv in the same directory of "hello" project
#
##########################################################################################

if [[ $# > 0 && $1 != "-"* ]]; then
  PRJ=$1
else
  PRJ="test"
fi

# strip 
PRJ=${PRJ%%/*}

# build project
VEX_DEP="NO"
VEX_TYP="EXT"
VEX_TOG="//"
GIT_TOG="//"
EXT_TOG="//"

VEX_EXT_PATH="./ext/VexRiscv"
VEX_GIT_PATH="https://github.com/SpinalHDL/VexRiscv.git"

argc=0
args=("$@")

VEX_SRC=""
for argv in $*
do
  let "argc+=1"
  [[ "$argv" == "-vex" ]] && VEX_DEP="YES"
  [[ "$argv" == "-git" ]] && VEX_TYP="GIT" && VEX_SRC="${args[$argc]}" 
  [[ "$argv" == "-ext" ]] && VEX_TYP="EXT" && VEX_SRC="${args[$argc]}" 
done

[[ "$VEX_DEP" == "YES" ]] && VEX_TOG=""
[[ "$VEX_DEP" == "YES" && "$VEX_TYP" == "GIT" ]] && GIT_TOG="" && EXT_TOG="//"
[[ "$VEX_DEP" == "YES" && "$VEX_TYP" == "EXT" ]] && EXT_TOG="" && GIT_TOG="//"

[[ "$VEX_SRC" != "" && "$VEX_TYP" == "EXT" ]] && VEX_EXT_PATH="$VEX_SRC" 
[[ "$VEX_SRC" != "" && "$VEX_TYP" == "GIT" ]] && VEX_GIT_PATH="$VEX_SRC" 

SPINAL_SBT="val spinalVersion = \"1.4.0\"

lazy val root = (project in file(\".\"))
  .settings(
    inThisBuild(List(
      organization := \"com.github.spinalhdl\",
      scalaVersion := \"2.11.12\",
      version      := \"1.0.0\"
    )),
    name := \"$PRJ\",
    libraryDependencies ++= Seq(
      \"com.github.spinalhdl\" % \"spinalhdl-core_2.11\" % spinalVersion,
      \"com.github.spinalhdl\" % \"spinalhdl-lib_2.11\" % spinalVersion,
      compilerPlugin(\"com.github.spinalhdl\" % \"spinalhdl-idsl-plugin_2.11\" % spinalVersion)
    )
  )${VEX_TOG}.dependsOn(vexRiscv)

//For dependancies localy on your computer :
${EXT_TOG}lazy val vexRiscv = RootProject(file(\"${VEX_EXT_PATH}\"))

//For dependancies on a git :
${GIT_TOG}lazy val vexRiscv = RootProject(uri(\"${VEX_GIT_PATH}\"))

fork := true"

if [[ -d $PRJ ]]; then
  read -p "Caution: directory [$PRJ] exist, continue? (y/n): " input
  if [[ ${input,,} == *"y"* ]]; then
    rm -rf $PRJ
  else
    exit 1
  fi
fi

mkdir -p $PRJ/project
mkdir -p $PRJ/src/main/scala
mkdir -p $PRJ/src/main/resources
mkdir -p $PRJ/src/test/scala
mkdir -p $PRJ/src/test/resources

echo -e "$SPINAL_SBT" > $PRJ/build.sbt
#sed -i s/\$PRJ/"$PRJ"/g $PRJ/build.sbt

echo "sbt.version = 1.4.5" > $PRJ/project/build.properties

VEX_DEMO="package demo

import spinal.core._
import vexriscv.demo._

object Briey{
  def main(args: Array[String]) {
    val config = SpinalConfig()
    config.generateVerilog({
      val toplevel = new Briey(BrieyConfig.default)
      toplevel
    })
    println(\"DONE\")
  }
}"

SPINAL_DEMO="/*
 * SpinalHDL
 * Copyright (c) Dolu, All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */

package mylib

import spinal.core._
import spinal.lib._

import scala.util.Random

//Hardware definition
class MyTopLevel extends Component {
  val io = new Bundle {
    val cond0 = in  Bool
    val cond1 = in  Bool
    val flag  = out Bool
    val state = out UInt(8 bits)
  }
  val counter = Reg(UInt(8 bits)) init(0)

  when(io.cond0){
    counter := counter + 1
  }

  io.state := counter
  io.flag  := (counter === 0) | io.cond1
}

//Generate the MyTopLevel's Verilog
object MyTopLevelVerilog {
  def main(args: Array[String]) {
    SpinalVerilog(new MyTopLevel)
  }
}

//Generate the MyTopLevel's VHDL
object MyTopLevelVhdl {
  def main(args: Array[String]) {
    SpinalVhdl(new MyTopLevel)
  }
}


//Define a custom SpinalHDL configuration with synchronous reset instead of the default asynchronous one. This configuration can be resued everywhere
object MySpinalConfig extends SpinalConfig(defaultConfigForClockDomains = ClockDomainConfig(resetKind = SYNC))

//Generate the MyTopLevel's Verilog using the above custom configuration.
object MyTopLevelVerilogWithCustomConfig {
  def main(args: Array[String]) {
    MySpinalConfig.generateVerilog(new MyTopLevel)
  }
}"

SPINALSIM_DEMO="package mylib

import spinal.core._
import spinal.sim._
import spinal.core.sim._

import scala.util.Random


//MyTopLevel's testbench
object MyTopLevelSim {
  def main(args: Array[String]) {
    SimConfig.withWave.doSim(new MyTopLevel){dut =>
      //Fork a process to generate the reset and the clock on the dut
      dut.clockDomain.forkStimulus(period = 10)

      var modelState = 0
      for(idx <- 0 to 99){
        //Drive the dut inputs with random values
        dut.io.cond0 #= Random.nextBoolean()
        dut.io.cond1 #= Random.nextBoolean()

        //Wait a rising edge on the clock
        dut.clockDomain.waitRisingEdge()

        //Check that the dut values match with the reference model ones
        val modelFlag = modelState == 0 || dut.io.cond1.toBoolean
        assert(dut.io.state.toInt == modelState)
        assert(dut.io.flag.toBoolean == modelFlag)

        //Update the reference model value
        if(dut.io.cond0.toBoolean) {
          modelState = (modelState + 1) & 0xFF
        }
      }
    }
  }
}"

######################################################################
#enter to new project
######################################################################
cd $PRJ

if [[ "$VEX_DEP" == "NO" ]]; then
  echo -e "$SPINAL_DEMO" > src/main/scala/MyTopLevel.scala
  echo -e "$SPINALSIM_DEMO" > src/main/scala/MyTopLevelSim.scala
else
  echo -e "$VEX_DEMO" > src/main/scala/BrieyDemo.scala

  if [[ "$VEX_TYP" == "EXT" && ! -d "$VEX_EXT_PATH" ]]; then
    echo "VexRiscv is not found in path: ${VEX_EXT_PATH} !!"
    
    read -p "Would you like to get a clone? (y/n): " yon
    
    if [[ ${yon,,} == *"y"* ]]; then
      git clone https://gitee.com/ic-starter/VexRiscv ${VEX_EXT_PATH}
    fi 
  fi
fi

echo "project: $PRJ initilized"

[[ "$*" == *"-run"* ]] && echo "Try to run..." && sbt run
